﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using SlimDX;
using SlimDX.Direct3D9;

using System.Windows.Forms;
using System.Runtime.Serialization;
using System.Security.Permissions;
using System.IO;
using System.Xml;
using System.Xml.Serialization;

namespace Splino.D3D
{
    /// <summary>
    /// A Splino Object defined by a single Vector3 as its Center property
    /// </summary>
    [Serializable()]
    public class PointObject : SplinoObject
    {
        /// <summary>
        /// The point's location in world coordinates
        /// </summary>
        protected Vector3 _center;
        /// <summary>
        /// Gets or sets the location of the point, updating
        /// the vertex buffer with the new location
        /// </summary>
        public virtual Vector3 Center
        {
            get { return Vector3.TransformCoordinate(_center, _transformMatrix); }
            set
            {
                Dispose();
                _center = value;
                _vertexList[0].Position = _center;
                _vertexList[0].Color = _currentColor.ToArgb();

                _transformMatrix = Matrix.Identity;

                ResetBuffers();

                DeviceManager.Instance.RequestRender();
            }
        }

        /// <summary>
        /// Constructor
        /// </summary>
        public PointObject()
            : base()
        {
            _type = SplinoObjectType.Point;
            _color = Palette.PointObject;
            _vertexCount = 1;
            _vertexList = new CustomVertex.PositionColored[1];
            _treeNodeImageKey = "point";
        }

        /// <summary>
        /// Protected constructor used for creating Point Objects from
        /// clipboard data objects
        /// </summary>
        /// <param name="info">Clipboard Serialization Info</param>
        /// <param name="context">Clipboard Streaming Context</param>
        protected PointObject(SerializationInfo info, StreamingContext context)
            : base(info, context)
        {
            _type = SplinoObjectType.Point;
            _vertexCount = 1;
            _vertexList = new CustomVertex.PositionColored[1];

            Center = (Vector3)info.GetValue("Center", typeof(Vector3));
        }

        /// <summary>
        /// Serializes the point for use in a clipboard data object for copy
        /// and paste operations
        /// </summary>
        /// <param name="serializationInfo">Clipboard Serialization Info</param>
        /// <param name="streamingContext">Clipboard Streaming Context</param>
        [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
        public override void GetObjectData(SerializationInfo serializationInfo,
            StreamingContext streamingContext)
        {
            base.GetObjectData(serializationInfo, streamingContext);

            serializationInfo.AddValue("Center", _center, typeof(Vector3));
        }

        /// <summary>
        /// Draws the Point Object to the Viewport
        /// </summary>
        public override void Draw(DrawLayer drawLayer, SplinoViewport splinoViewport)
        {
            base.Draw(drawLayer, splinoViewport);

            if (_drawLayer == drawLayer)
            {
                //DataStream stream = _vertexBuffer.Lock(0, 0, LockFlags.None);
                //stream.WriteRange(_vertexList);
                //_vertexBuffer.Unlock();
                DeviceManager.Instance.Device.SetStreamSource(
                    0,
                    _vertexBuffer,
                    0,
                    CustomVertex.PositionColored.SizeInBytes);
                DeviceManager.Instance.Device.DrawPrimitives(PrimitiveType.PointList, 0, 1);
            }
        }

        /// <summary>
        /// <para>Shoots a ray from a starting position vector and a unit vector
        /// direction, and tests to see if it intersects with this Splino Object,
        /// returning true if it does intersect and false if it does not.  Used
        /// for picking/selecting objects.</para>
        /// <para>For Point Objects, the ray is tested against a simple box
        /// mesh that is scaled according to UnitsPerPixel to keep the box
        /// the same relative size at all zoom levels</para>
        /// </summary>
        /// <param name="viewport">The viewport that contains the picking ray and 
        /// camera used for the picking test</param>
        /// <param name="rayPosition">The start position of the picking ray, generally
        /// the camera's location subtracted from an unprojected mouse click</param>
        /// <param name="rayDirection">A unit vector </param>
        /// <returns>True if the picking ray intersected the object, and false
        /// if it did not</returns>
        public override List<SplinoObject> IntersectRay(SplinoViewport viewport,
            Vector3 rayPosition, Vector3 rayDirection)
        {
            List<SplinoObject> splinoObjects = new List<SplinoObject>();

            Mesh boundingBoxMesh = Mesh.CreateBox(
                DeviceManager.Instance.Device,
                viewport.UnitsPerPixel(_center) * 10f,
                viewport.UnitsPerPixel(_center) * 10f,
                viewport.UnitsPerPixel(_center) * 10f);

            rayPosition = Vector3.TransformCoordinate(rayPosition, Matrix.Translation(Vector3.Multiply(_center, -1.0f)));

            if (boundingBoxMesh.Intersects(new Ray(rayPosition, rayDirection)))
                splinoObjects.Add(this);

            boundingBoxMesh.Dispose();

            return splinoObjects;
        }

        /// <summary>
        /// Tests the object to see if any part of it is clipped by the selection
        /// rectangle in a given Splino Viewport, returning true if the object
        /// was clipped by the rectangle (or completely contained by), and false
        /// if it is not.  Used for multiple object picking/selection
        /// </summary>
        /// <param name="splinoViewport">The viewport containing the selection
        /// rectangle</param>
        /// <returns>True if the object was clipped by the selection rectangle,
        /// false if it was not</returns>
        public override List<SplinoObject> IntersectInclusive(SplinoViewport splinoViewport)
        {
            List<SplinoObject> splinoObjects = new List<SplinoObject>();

            if (splinoViewport.PointInFustrum(_center))
                splinoObjects.Add(this);
            return splinoObjects;
        }

        /// <summary>
        /// Tests the object to see if it is completely contained by the
        /// selection rectangle in a given Splino Viewport, returning true
        /// if it is, and false if it is not.  Used for multiple object
        /// selection/picking
        /// </summary>
        /// <param name="splinoViewport">The Splino Viewport containg
        /// the selection rectangle</param>
        /// <returns>True if this object is completely contained by
        /// the Viewport's selection rectangle, false if it is not</returns>
        public override List<SplinoObject> IntersectExclusive(SplinoViewport splinoViewport)
        {
            List<SplinoObject> splinoObjects = new List<SplinoObject>();
            if (splinoViewport.PointInFustrum(_center))
                splinoObjects.Add(this);
            return splinoObjects;
        }

        /// <summary>
        /// Shoots a ray from the drawing point through the scene, testing for intersections
        /// with an object and its children, and returns a list of snap type and location pairs, if
        /// any exist.
        /// </summary>
        /// <param name="splinoViewport">The viewport to shoot the ray through</param>
        /// <param name="rayPosition">The start of the ray</param>
        /// <param name="rayDirection">The ray's direction</param>
        /// <returns>A list of snap type and location pairs</returns>
        public override List<ObjectSnap> SnapTest(SplinoViewport splinoViewport, Vector3 rayPosition, Vector3 rayDirection)
        {
            List<ObjectSnap> objectSnaps = new List<ObjectSnap>();
            ObjectSnap objectSnap;

            if (IntersectRay(splinoViewport, rayPosition, rayDirection).Contains(this))
            {
                objectSnap.ObjectSnapType = ObjectSnapType.Point;
                objectSnap.SnapLocation = _center;
                objectSnaps.Add(objectSnap);
            }
            _activeObjectSnaps = objectSnaps;
            return objectSnaps;
        }

        /// <summary>
        /// Transfers the transformation matrix to the object, and resets
        /// the transformation matrix back to the identity matrix
        /// </summary>
        public override void FinalizeTransform()
        {
            Center = Vector3.TransformCoordinate(_center, _transformMatrix);
            base.FinalizeTransform();
        }

        //public override void Transform(Matrix transform)
        //{
        //    base.Transform(transform);
        //    FinalizeTransform();
        //}
    }
}
